/*
* Sun Public License Notice
*
* The contents of this file are subject to the Sun Public License
* Version 1.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://www.sun.com/
*
* The Original Code is Forte for Java, Community Edition. The Initial
* Developer of the Original Code is Sun Microsystems, Inc. Portions
* Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved.
*/
package org.netbeans.editor;
import java.util.ArrayList;
import java.util.Arrays;
import javax.swing.text.JTextComponent;
import javax.swing.KeyStroke;
import javax.swing.Action;
/**
* Extension of JTextComponent.KeyBinding to hold several successive keystrokes.
* The binding containing null key(s) is assumed to assign the default action.
*
* @author Miloslav Metelka
* @version 1.00
*/
public class MultiKeyBinding extends JTextComponent.KeyBinding
implements java.io.Externalizable {
/** Successive keystroke. They must be pressed in the order they
* are stored in the array in order to invoke the associated action.
*/
public KeyStroke[] keys;
static final long serialVersionUID =-8602816556604003688L;
/** Constructor for serialization */
public MultiKeyBinding() {
super(null, null);
}
/** Constructor for assigning keystroke sequence to action
* @param keys successive keystroke that must be pressed in order
* to invoke action
* @param actionName action that will be invoked. Action is resolved
* from name by calling kit.getActions() after the kit is constructed
*/
public MultiKeyBinding(KeyStroke[] keys, String actionName) {
super(null, actionName);
this.keys = keys;
}
/** Compatibility constructor */
public MultiKeyBinding(KeyStroke key, String actionName) {
super(key, actionName);
}
/** Constructor for existing KeyBinding */
public MultiKeyBinding(JTextComponent.KeyBinding kb) {
this(kb.key, kb.actionName);
}
public boolean equals(Object o) {
if (o instanceof MultiKeyBinding) {
MultiKeyBinding kb = (MultiKeyBinding)o;
// Compare action names
if (actionName == null) {
if (kb.actionName != null) {
return false;
}
} else {
if (!actionName.equals(kb.actionName)) {
return false;
}
}
// Action names match, now compare action keys
if (keys == null) {
if (kb.keys == null) {
return (key == null && kb.key == null)
|| (key != null && key.equals(kb.key));
} else {
return (kb.keys.length == 1
&& ((key == null && kb.keys[0] == null)
|| (key != null && key.equals(kb.keys[0]))));
}
} else { // keys != null
if (kb.keys != null) {
return Arrays.equals(keys, kb.keys);
} else { // kb.keys == null
return (keys.length == 1
&& ((kb.key == null && keys[0] == null)
|| (kb.key != null && kb.key.equals(keys[0]))));
}
}
}
return false;
}
/** Add or replace key bindings array by changes given in
* the second bindings array
* @param target target list of bindings
* @param changes list of changes to apply:
* binding containing the non-null keystroke(s) and non-null action
* will add the binding or replace the old binding with the same
* keystroke(s) in the target array,
* binding of the non-null keystroke(s) and null action removes
* the binding for that keystroke from the target array (if it existed)
* binding containing null keystroke and non-null action adds
* or replaces default action
*/
public static void updateKeyBindings(JTextComponent.KeyBinding[] target,
JTextComponent.KeyBinding[] changes) {
ArrayList tgt = new ArrayList(Arrays.asList(target));
MultiKeyBinding tmp = new MultiKeyBinding(new KeyStroke[1], null);
MultiKeyBinding cur;
for (int i = 0; i < changes.length; i++) {
if (changes[i] instanceof MultiKeyBinding) {
cur = (MultiKeyBinding)changes[i];
if (cur.keys == null) { // single key multi binding
tmp.keys[0] = cur.key;
tmp.actionName = cur.actionName;
cur = tmp;
}
} else { // simulate multi binding
tmp.keys[0] = changes[i].key;
tmp.actionName = changes[i].actionName;
cur = tmp;
}
// cycle through all bindings
boolean matched = false;
for (int j = 0; j < tgt.size(); j++) {
JTextComponent.KeyBinding kb = (JTextComponent.KeyBinding)tgt.get(j);
if (kb instanceof MultiKeyBinding) {
MultiKeyBinding mkb = (MultiKeyBinding)kb;
if (mkb.keys == null) { // single key multi binding
if (cur.keys.length == 1 && cur.keys[0].equals(mkb.key)) { // found
if (mkb.actionName == null) { // remove
tgt.remove(i);
} else { // replace
tgt.set(i, mkb);
}
matched = true;
break;
}
} else { // multi binding
if (cur.keys.length == mkb.keys.length) {
matched = true;
for (int k = 0; k < cur.keys.length; k++) {
if (!cur.keys[k].equals(mkb.keys[k])) {
matched = false;
break;
}
}
if (matched) {
if (mkb.actionName == null) { // remove
tgt.remove(i);
} else { // replace
tgt.set(i, mkb);
}
break;
}
}
}
} else { // single key binding
if (cur.keys.length == 1 && cur.keys[0].equals(kb.key)) { // found
if (kb.actionName == null) { // remove
tgt.remove(i);
} else { // replace
tgt.set(i, kb);
}
matched = true;
break;
}
}
}
if (!matched) {
tgt.add(changes[tgt.size()]);
}
}
}
public void readExternal(java.io.ObjectInput in)
throws java.io.IOException, ClassNotFoundException {
Object obj = in.readObject ();
if( obj instanceof Integer ) { // new settings format
int len = ((Integer)obj).intValue();
if( len >= 0 ) {
keys = new KeyStroke[ len ];
for( int i=0; i<len; i++ ) {
keys[i] = KeyStroke.getKeyStroke( in.readInt(), in.readInt(), in.readBoolean() );
}
} else {
keys = null;
}
if( in.readBoolean() ) {
key = KeyStroke.getKeyStroke( in.readInt(), in.readInt(), in.readBoolean() );
} else {
key = null;
}
actionName = (String)in.readObject();
} else { // compatibility mode, settings in old format
keys = (KeyStroke[])obj;
key = (KeyStroke)in.readObject();
actionName = (String)in.readObject();
}
}
public void writeExternal(java.io.ObjectOutput out)
throws java.io.IOException {
if( keys != null ) {
out.writeObject( new Integer( keys.length ) );
for( int i=0; i<keys.length; i++ ) {
out.writeInt( keys[i].getKeyCode() );
out.writeInt( keys[i].getModifiers() );
out.writeBoolean( keys[i].isOnKeyRelease() );
}
} else {
out.writeObject( new Integer( -1 ) );
}
if( key != null ) {
out.writeBoolean( true );
out.writeInt( key.getKeyCode() );
out.writeInt( key.getModifiers() );
out.writeBoolean( key.isOnKeyRelease() );
} else {
out.writeBoolean( false );
}
out.writeObject( actionName );
}
public String toString() {
if (keys == null) {
return "key=" + key + ", actionName=" + actionName; // NOI18N
} else {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < keys.length; i++) {
sb.append("key"); // NOI18N
sb.append(i);
sb.append('=');
sb.append(keys[i]);
sb.append(", "); // NOI18N
}
sb.append("actionName="); // NOI18N
sb.append(actionName);
return sb.toString();
}
}
}
/*
* Log
* 11 Gandalf-post-FCS1.8.1.1 3/1/00 Petr Nejedly
* 10 Gandalf-post-FCS1.8.1.0 2/28/00 Petr Nejedly Made serialized data
* compatible across JDKs
* 9 Gandalf 1.8 1/13/00 Miloslav Metelka
* 8 Gandalf 1.7 10/23/99 Ian Formanek NO SEMANTIC CHANGE - Sun
* Microsystems Copyright in File Comment
* 7 Gandalf 1.6 8/27/99 Miloslav Metelka
* 6 Gandalf 1.5 8/18/99 Miloslav Metelka
* 5 Gandalf 1.4 8/9/99 Ian Formanek Generated Serial Version
* UID
* 4 Gandalf 1.3 7/29/99 Miloslav Metelka
* 3 Gandalf 1.2 7/26/99 Miloslav Metelka
* 2 Gandalf 1.1 3/27/99 Miloslav Metelka
* 1 Gandalf 1.0 3/23/99 Miloslav Metelka
* $
*/